package rep

import (
	"bufio"
	"fmt"
	"io"
	"strings"

	"gitcode.com/deyiyangyang/bampoo/evl"
	"gitcode.com/deyiyangyang/bampoo/lex"
	"gitcode.com/deyiyangyang/bampoo/obj"
	"gitcode.com/deyiyangyang/bampoo/par"
)

const PROMPT = "bamboo> "

// REPL represents a Read-Eval-Print Loop
type REPL struct {
	evaluator *evl.Evaluator
}

// New creates a new REPL
func New(evaluator *evl.Evaluator) *REPL {
	return &REPL{evaluator: evaluator}
}

// Start starts the REPL
func (r *REPL) Start(in io.Reader, out io.Writer) {
	scanner := bufio.NewScanner(in)

	for {
		fmt.Fprintf(out, PROMPT)
		scanned := scanner.Scan()
		if !scanned {
			return
		}

		line := scanner.Text()
		if strings.TrimSpace(line) == "" {
			continue
		}

		// Handle multi-line input (simple approach)
		input := line
		if !r.isBalanced(input) {
			for {
				fmt.Fprintf(out, "     > ")
				scanned := scanner.Scan()
				if !scanned {
					return
				}
				line := scanner.Text()
				input += " " + line
				if r.isBalanced(input) {
					break
				}
			}
		}

		result, err := r.evaluate(input)
		if err != nil {
			fmt.Fprintf(out, "Error: %s\n", err.Error())
		} else if result != nil {
			fmt.Fprintf(out, "%s\n", result.String())
		}
	}
}

func (r *REPL) evaluate(input string) (obj.Value, error) {
	// Lexical analysis
	lexer := lex.New(input)

	// Syntax analysis
	parser := par.New(lexer)
	program := parser.ParseProgram()

	if len(parser.Errors()) > 0 {
		return nil, fmt.Errorf("parse errors: %s", strings.Join(parser.Errors(), "; "))
	}

	// Evaluation
	return r.evaluator.Eval(program)
}

// isBalanced checks if parentheses are balanced (simple implementation)
func (r *REPL) isBalanced(input string) bool {
	count := 0
	inString := false
	escaped := false

	for _, ch := range input {
		if inString {
			if escaped {
				escaped = false
				continue
			}
			if ch == '\\' {
				escaped = true
				continue
			}
			if ch == '"' {
				inString = false
			}
			continue
		}

		if ch == '"' {
			inString = true
			continue
		}

		switch ch {
		case '(':
			count++
		case ')':
			count--
			if count < 0 {
				return false
			}
		}
	}

	return count == 0 && !inString
}
